﻿namespace JoinBox.Basal;

using System;
using System.Runtime.InteropServices;

/// <summary>
/// 共享内存
/// </summary>
public class ShareMem : IDisposable
{
    /// <summary>
    /// 创建一个文件映射内核对象
    /// </summary>
    [DllImport("Kernel32.dll", CharSet = CharSet.Auto)]
    public static extern IntPtr CreateFileMapping(int hFile, IntPtr lpAttributes, uint flProtect, uint dwMaxSizeHi, uint dwMaxSizeLow, string lpName);
    /// <summary>
    /// 创建内存映射
    /// </summary>
    [DllImport("Kernel32.dll", CharSet = CharSet.Auto)]
    public static extern IntPtr MapViewOfFile(IntPtr hFileMapping, uint dwDesiredAccess, uint dwFileOffsetHigh, uint dwFileOffsetLow, uint dwNumberOfBytesToMap);
    /// <summary>
    /// 停止内存映射
    /// </summary>
    [DllImport("Kernel32.dll", CharSet = CharSet.Auto)]
    public static extern bool UnmapViewOfFile(IntPtr pvBaseAddress);

    const int ERROR_ALREADY_EXISTS = 183;
    const int FILE_MAP_WRITE = 0x0002;
    const int PAGE_READWRITE = 0x04;
    const int INVALID_HANDLE_VALUE = -1;
    IntPtr m_hSharedMemoryFile = IntPtr.Zero;
    IntPtr m_pwData = IntPtr.Zero;
    bool m_bAlreadyExist = false;
    bool m_bInit = false;
    long m_MemSize = 0;


    /// <summary>
    /// 共享内存<br/>
    /// <a href="https://www.cnblogs.com/lizhigang/p/7373224.html">本文链接</a>
    /// </summary>
    /// <param name="strName">空间名称</param>
    /// <param name="lngSize">大小</param>
    /// <returns></returns>
    public ShareMem(string strName, long lngSize)
    {
        if (strName == null || strName.Length < 1)
            throw new ArgumentNullException(nameof(strName));

        if (lngSize <= 0 || lngSize > 0x00800000)
            lngSize = 0x00800000;

        m_MemSize = lngSize;


        // 创建内存共享体(INVALID_HANDLE_VALUE)
        m_hSharedMemoryFile = CreateFileMapping(
                                    INVALID_HANDLE_VALUE,
                                    IntPtr.Zero,
                                    (uint)PAGE_READWRITE,
                                    0,
                                    (uint)lngSize,
                                    strName);

        if (m_hSharedMemoryFile == IntPtr.Zero)
        {
            m_bAlreadyExist = false;
            m_bInit = false;
            throw new ArgumentException("创建共享体失败");
        }

        if (WindowsAPI.GetLastError() == ERROR_ALREADY_EXISTS) // 已经创建
            m_bAlreadyExist = true;
        else
            m_bAlreadyExist = false;

        // 创建内存映射
        m_pwData = MapViewOfFile(m_hSharedMemoryFile, FILE_MAP_WRITE, 0, 0, (uint)lngSize);
        if (m_pwData == IntPtr.Zero)
        {
            m_bInit = false;
            WindowsAPI.CloseHandle(m_hSharedMemoryFile);
            throw new ArgumentException("创建内存映射失败");
        }

        m_bInit = true;
        if (m_bAlreadyExist == false)
        {
            // 初始化 ??
        }
    }


    /// <summary>
    /// 共享内存中读取数据
    /// </summary>
    /// <param name="bytData"></param>
    /// <param name="lngAddr"></param>
    /// <param name="lngSize"></param>
    /// <returns></returns>
    public void Read(ref byte[] bytData, int lngAddr, int lngSize)
    {
        if (lngAddr + lngSize > m_MemSize)
            throw new ArgumentException("超出数据区");

        if (!m_bInit)
            throw new ArgumentException("m_bInit == false");

        Marshal.Copy(m_pwData, bytData, lngAddr, lngSize);
    }


    /// <summary>
    /// 共享内存中写入数据
    /// </summary>
    /// <param name="bytData"></param>
    /// <param name="lngAddr"></param>
    /// <param name="lngSize"></param>
    /// <returns></returns>
    public void Write(byte[] bytData, int lngAddr, int lngSize)
    {
        if (lngAddr + lngSize > m_MemSize)
            throw new ArgumentException("超出数据区");

        if (!m_bInit)
            throw new ArgumentException("m_bInit == false");

        Marshal.Copy(bytData, lngAddr, m_pwData, lngSize);
    }

    /// <summary>
    /// 共享内存中写入数据
    /// </summary>
    /// <returns></returns>
    public void Clear()
    {
        if (!m_bInit)
            return;

        byte[] bytData = new byte[m_MemSize];
        Marshal.Copy(bytData, 0, m_pwData, (int)m_MemSize);
    }


    #region IDisposable接口相关函数
    public bool IsDisposed { get; private set; } = false;

    /// <summary>
    /// 手动调用释放
    /// </summary>
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    /// <summary>
    /// 析构函数调用释放
    /// </summary>
    ~ShareMem()
    {
        Dispose(false);
    }

    protected virtual void Dispose(bool disposing)
    {
        // 不重复释放,并设置已经释放
        if (IsDisposed) return;
        IsDisposed = true;

        // 释放共享内存
        if (m_bInit)
        {
            UnmapViewOfFile(m_pwData);
            WindowsAPI.CloseHandle(m_hSharedMemoryFile);
        }
    }
    #endregion
}